#include "conversemessagecontainer.h"
#include "converseabstractmessage.h"
#include "conversemessagegroup.h"
#include "utils/timeutility.h"


#include <QDebug>


namespace Converse {

MessageContainer::MessageContainer(QObject *parent) :
    QObject(parent)
{
}


MessageContainer::~MessageContainer()
{
    qDeleteAll(m_dataItemList);
    m_dataItemList.clear();
}


const QList<MessageGroup *> &MessageContainer::dataItemList() const
{
    return m_dataItemList;
}


int MessageContainer::insert(AbstractMessage *message)
{
    if (message == NULL)
        return -1;

    if (m_dataItemList.isEmpty()) {
        MessageGroup *newMessageGroup = new MessageGroup(message);
        appendItem(newMessageGroup);
        return 0;
    }

    int position = m_dataItemList.size() - 1;
    MessageGroup *positionMessageGroup = NULL;

    for(; position >= 0; --position) {
        positionMessageGroup = m_dataItemList.at(position);
        Q_ASSERT(positionMessageGroup);

        // Find first messageGroup that its maximum of created time is less
        // than created time of the message
        if (message->createdTime() >= positionMessageGroup->maximumCreatedTime())
            break;
    }

    if (position == m_dataItemList.size() - 1) {

        // Create a new Group if senderId of the message differs from senderId of the messageGroup
        // or different between created time of the message and
        // maximum created time of the message Group is greater than a day time period
        if ((message->senderId() != positionMessageGroup->senderId()) || \
                qAbs(message->createdTime() - positionMessageGroup->maximumCreatedTime()) >= \
                Utility::TimeUtility::secsTimePeriodDay()) {

            MessageGroup *newMessageGroup = new MessageGroup(message);
            appendItem(newMessageGroup);
            return position + 1;
        }

        // Insert to position Group
        updateItem(message, positionMessageGroup);
        return position;
    }

    if (position == -1) {
        if ((message->senderId() != positionMessageGroup->senderId()) ||
                qAbs(message->createdTime() - positionMessageGroup->maximumCreatedTime()) >= \
                Utility::TimeUtility::secsTimePeriodDay()) {

            MessageGroup *newMessageGroup = new MessageGroup(message);
            insertItem(0, newMessageGroup);
            return 0;
        }

        // Insert to the first Group
        updateItem(message, positionMessageGroup);
        return 0;
    }

    MessageGroup *nextPositionMessageGroup = m_dataItemList.at(position + 1);
    Q_ASSERT(nextPositionMessageGroup);
    if (message->createdTime() >= nextPositionMessageGroup->minimumCreatedTime()) {
        if (message->senderId() != nextPositionMessageGroup->senderId()) {
            qDebug() << "Can not insert the invalid Message!";
            return -1; // Invalid message
        }

        updateItem(message, nextPositionMessageGroup);
        return position + 1;
    }

    // Check if message contained in the position Group
    if (qAbs(message->createdTime() - positionMessageGroup->maximumCreatedTime()) < \
            Utility::TimeUtility::secsTimePeriodDay() &&
            (message->senderId() == positionMessageGroup->senderId())) {

        updateItem(message, positionMessageGroup);
        return position;
    }

    // create new Group that contains the message
    MessageGroup *newMessageGroup = new MessageGroup(message);
    insertItem(position + 1, newMessageGroup);
    return position + 1;
}


void MessageContainer::insert(MessageGroup *messageGroup)
{
    if (messageGroup == NULL)
        return;

    int position = m_dataItemList.size() - 1;
    MessageGroup *positionMessageGroup = NULL;
    for(; position >= 0; --position) {
        positionMessageGroup = m_dataItemList[position];
        Q_ASSERT(positionMessageGroup);

        // Find the first messageGroup that its maximum created time is less
        // than created time of the message
        if (messageGroup->minimumCreatedTime() >= positionMessageGroup->maximumCreatedTime())
            break;
    }

    if (position == m_dataItemList.size() - 1) {
        appendItem(messageGroup);
        return;
    }

    MessageGroup *nextPositionMessageGroup = m_dataItemList.at(position + 1);
    Q_ASSERT(nextPositionMessageGroup);
    if (messageGroup->maximumCreatedTime() > nextPositionMessageGroup->minimumCreatedTime())
        return; // Invalid message Group

    insertItem(position + 1, messageGroup);
}


void MessageContainer::append(AbstractMessage *message)
{
    if (message == NULL)
        return;

    if (m_dataItemList.isEmpty()) {
        MessageGroup *newMessageGroup = new MessageGroup(message);
        appendItem(newMessageGroup);
        return;
    }

    MessageGroup *lastMessageGroup = m_dataItemList.last();

    // Create a new Group if senderId of the message differs from senderId of the messageGroup
    // or different between created time of the message and
    // maximum created time of the message Group is greater than a day time period
    if ((message->senderId() != lastMessageGroup->senderId()) || \
            qAbs(message->createdTime() - lastMessageGroup->maximumCreatedTime()) >= \
            Utility::TimeUtility::secsTimePeriodDay()) {

        MessageGroup *newMessageGroup = new MessageGroup(message);
        appendItem(newMessageGroup);
        return;
    }

    // Append to the last Group
    updateItem(message, lastMessageGroup);
}


void MessageContainer::append(MessageGroup *messageGroup)
{
    if (messageGroup == NULL)
        return;

    appendItem(messageGroup);
}


void MessageContainer::itemAboutToBeInserted(int index, MessageGroup *messageGroup)
{
    if (messageGroup == NULL)
        return;

    if ((index < 0) || (index > count()))
        return;

    emit dataAboutToBeInserted(index, messageGroup);
}


void MessageContainer::itemInserted(int index, MessageGroup *messageGroup)
{
    if (messageGroup == NULL)
        return;

    if ((index < 0) || (index > count()))
        return;

    emit dataInserted(index, messageGroup);
}


void MessageContainer::itemAboutToBeUpdated(int index, MessageGroup *messageGroup)
{
    if (messageGroup == NULL)
        return;

    if (!validIndex(index))
        return;

    emit dataAboutToBeUpdated(index, messageGroup);
}


void MessageContainer::itemUpdated(int index, MessageGroup *messageGroup)
{
    if (messageGroup == NULL)
        return;

    if (!validIndex(index))
        return;

    emit dataUpdated(index, messageGroup);
}


void MessageContainer::insertItem(int index, MessageGroup *messageGroup)
{
    QMutexLocker locker(&m_mutex);
    if (messageGroup == NULL)
        return;

    if ((index < 0) || (index > count()))
        return;

    if (contains(messageGroup))
        return;

    itemAboutToBeInserted(index, messageGroup);
    m_dataItemList.insert(index, messageGroup);
    itemInserted(index, messageGroup);
}


void MessageContainer::appendItem(MessageGroup *messageGroup)
{
    QMutexLocker locker(&m_mutex);
    if (messageGroup == NULL)
        return;

    if (contains(messageGroup))
        return;

    int index = this->size();
    itemAboutToBeInserted(index, messageGroup);
    m_dataItemList.append(messageGroup);
    itemInserted(index, messageGroup);
}


void MessageContainer::updateItem(AbstractMessage *message, MessageGroup *messageGroup)
{
    QMutexLocker locker(&m_mutex);
    if (message == NULL)
        return;

    if (!contains(messageGroup))
        return;

    int index = this->indexOf(messageGroup);
    itemAboutToBeUpdated(index, messageGroup);
    messageGroup->insert(message);
    itemUpdated(index, messageGroup);
}


bool MessageContainer::validIndex(int index) const
{
    if ((index < 0) || index >= count())
        return false;

    return true;
}


bool MessageContainer::contains(MessageGroup *messageGroup) const
{
    if (messageGroup == NULL)
        return false;

    return m_dataItemList.contains(messageGroup);
}


MessageGroup *MessageContainer::at(int index) const
{
    if (!validIndex(index))
        return NULL;

    return m_dataItemList.at(index);
}


int MessageContainer::indexOf(MessageGroup *messageGroup) const
{
    if (messageGroup == NULL)
        return -1;

    return m_dataItemList.indexOf(messageGroup);
}


int MessageContainer::count() const
{
    return m_dataItemList.count();
}


int MessageContainer::size() const
{
    return m_dataItemList.size();
}

} // namespace Converse
